﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NpgsqlTypes;
using System.Data.Common;
using Npgsql;

namespace DbSharp.Framework.Driver.FieldPersistence
{
    class PostgresqlFieldPersistence : IFieldPersistence
    {
        #region IFieldPersistence<NpgsqlDbType> Members

        public List<int> ListSupportedScalarDataType
        {

            get
            {
                var list = new List<int>();
                list.Add((int)NpgsqlDbType.Bigint);
                list.Add((int)NpgsqlDbType.Boolean);
                list.Add((int)NpgsqlDbType.Char);
                list.Add((int)NpgsqlDbType.Date);
                list.Add((int)NpgsqlDbType.Double);
                list.Add((int)NpgsqlDbType.Integer);
                list.Add((int)NpgsqlDbType.Money);
                list.Add((int)NpgsqlDbType.Numeric);
                list.Add((int)NpgsqlDbType.Real);
                list.Add((int)NpgsqlDbType.Smallint);
                list.Add((int)NpgsqlDbType.Text);
                list.Add((int)NpgsqlDbType.Time);
                list.Add((int)NpgsqlDbType.Timestamp);
                list.Add((int)NpgsqlDbType.Varchar);
                list.Add((int)NpgsqlDbType.Inet);
                list.Add((int)NpgsqlDbType.Bit);
                list.Add((int)NpgsqlDbType.TimestampTZ);
                list.Add((int)NpgsqlDbType.Uuid);
                list.Add((int)NpgsqlDbType.Interval);
                list.Add((int)NpgsqlDbType.TimeTZ);
                list.Add((int)NpgsqlDbType.Name);
                list.Add((int)NpgsqlDbType.Varchar);

                return list;
            }
        }




        public List<int> ListSupportedRecordsetDataType
        {
            get
            {
                var list = new List<int>();
                list.Add((int)NpgsqlDbType.Refcursor);
                return list;
            }
        }

        public string ConvertToString(object value, int valueType)
        {
            var valueType1 = (NpgsqlDbType)valueType;
            var exceptionFmt = "{0} type is not supported.";
            var exceptionMsg = string.Format(exceptionFmt, valueType1.ToString());
            if (!ListSupportedScalarDataType.Contains(valueType))
            {
                throw new NotSupportedException(exceptionMsg);
            }

            string result;
            switch (valueType1)
            {
                case NpgsqlDbType.Bigint:
                case NpgsqlDbType.Boolean:
                case NpgsqlDbType.Char:
                case NpgsqlDbType.Date:
                case NpgsqlDbType.Double:
                case NpgsqlDbType.Integer:
                case NpgsqlDbType.Money:
                case NpgsqlDbType.Numeric:
                case NpgsqlDbType.Real:
                case NpgsqlDbType.Smallint:
                case NpgsqlDbType.Text:
                case NpgsqlDbType.Time:
                case NpgsqlDbType.Timestamp:
                case NpgsqlDbType.Varchar:
                case NpgsqlDbType.Inet:
                case NpgsqlDbType.Bit:
                case NpgsqlDbType.TimestampTZ:
                case NpgsqlDbType.Uuid:
                case NpgsqlDbType.Interval:
                case NpgsqlDbType.TimeTZ:
                case NpgsqlDbType.Name:
                    result = FieldPersistenceHelper.SafeConvertToString(value);
                    break;
                default:
                    throw new NotSupportedException(exceptionMsg);
            }

            return result;
        }

        public decimal RestoreStringToDecimal(string valueStr, int valueType)
        {
            var valueType1 = (NpgsqlDbType)valueType;
            decimal result;
            switch (valueType1)
            {
                case NpgsqlDbType.Bigint:
                case NpgsqlDbType.Double:
                case NpgsqlDbType.Integer:
                case NpgsqlDbType.Money:
                case NpgsqlDbType.Numeric:
                case NpgsqlDbType.Real:
                case NpgsqlDbType.Smallint:
                case NpgsqlDbType.Interval:
                    result = Convert.ToDecimal(valueStr);
                    break;
                default:
                    throw new NotSupportedException(string.Format("{0} type is not supported.", valueType1.ToString()));
            }
            return result;
        }

        public Devart.Data.Oracle.OracleNumber RestoreStringToLargeDecimal(string valueStr, int valueType)
        {
            var valueType1 = (NpgsqlDbType)valueType;
            Devart.Data.Oracle.OracleNumber result;
            switch (valueType1)
            {
                case NpgsqlDbType.Bigint:
                case NpgsqlDbType.Double:
                case NpgsqlDbType.Integer:
                case NpgsqlDbType.Money:
                case NpgsqlDbType.Numeric:
                case NpgsqlDbType.Real:
                case NpgsqlDbType.Smallint:
                case NpgsqlDbType.Interval:
                    result = Devart.Data.Oracle.OracleNumber.Parse(valueStr);
                    break;
                default:
                    throw new NotSupportedException(string.Format("{0} type is not supported.", valueType1.ToString()));
            }
            return result;
        }



        public int GetSpecificDbType(DbDataReader dataReader, int columnIndex)
        {
            //class: Npgsql.NpgsqlDataReader
            //method: public NpgsqlDbType GetFieldNpgsqlDbType(int Index)


            //var method = dataReader.GetType().GetMethod("GetFieldNpgsqlDbType");
            //var parameters = new object[] { columnIndex };
            //return (int)method.Invoke(dataReader, parameters);

            var reader = (Npgsql.NpgsqlDataReader)dataReader;
            return (int)reader.GetFieldNpgsqlDbType(columnIndex);
        }
 

 

        public int GetSpecificDbType(DbParameter dbParam)
        {
            var typ = ((NpgsqlParameter)dbParam).NpgsqlDbType;
            return (int)typ;
        }


        public int NumberTypeWhenParseLiteral
        {
            get { return (int)(NpgsqlDbType.Numeric); }
        }


        public bool IsStringType(int valueType)
        {
            bool result = false;
            var valueType1 = (NpgsqlDbType)valueType;
            switch (valueType1)
            { 
                case NpgsqlDbType.Char: 
                case NpgsqlDbType.Text: 
                case NpgsqlDbType.Varchar:  
                case NpgsqlDbType.Name:
                    result = true;
                    break;
                default:
                    result = false;
                    break;
            }

            return result;
        }


        #endregion
    }
}
